package com.learn.security.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.learn.core.exception.CommonException;
import com.learn.security.api.dto.RegisterDTO;
import com.learn.security.app.service.LoginService;
import com.learn.security.domain.entity.Login;
import com.learn.security.infra.mapper.LoginMapper;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author PC
 * 登录实现类
 */
@Service
public class LoginServiceImpl implements LoginService {

    private final static Logger LOGGER = LoggerFactory.getLogger(LoginServiceImpl.class);
    private final LoginMapper loginMapper;

    private RedissonClient redissonClient;

    private PasswordEncoder passwordEncoder;

    public LoginServiceImpl(LoginMapper loginMapper) {
        this.loginMapper = loginMapper;
    }

    @Autowired
    private void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Autowired
    private void setRedissonClient(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    public Login getLoginInfo(String loginName) {
        QueryWrapper<Login> queryWrapper = new QueryWrapper<Login>().eq(Objects.nonNull(loginName), Login.FIELD_LOGIN_NAME, loginName);
        return loginMapper.selectOne(queryWrapper);
    }

    @Override
    @Transactional
    public RegisterDTO register(RegisterDTO registerDTO) {
        boolean validFlag = !(Objects.isNull(registerDTO) || StringUtils.isEmpty(registerDTO.getLoginName()) || StringUtils.isEmpty(registerDTO.getPassword())
                || StringUtils.isEmpty(registerDTO.getVerifyPassword()));
        //必要信息缺失
        Assert.isTrue(validFlag, "error.necessary.information.missing");
        //两次密码不一致
        Assert.isTrue(StringUtils.equals(registerDTO.getPassword(), registerDTO.getVerifyPassword()), "error.auth_password.wrong");
        //密码加密存储
        try {
            registerDTO.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
        } catch (Exception exception) {
            LOGGER.error("encode password:{} failed{}", registerDTO.getPassword(), exception.getMessage());
            throw new CommonException("error.password.encode");
        }

        RLock rLock = redissonClient.getLock("lock:" + registerDTO.getLoginName());
        try {
            //获取锁等待3秒，10秒过期
            if (rLock.tryLock(3, 10, TimeUnit.SECONDS)) {
                //数据库中是否有这个loginName
                if (Objects.isNull(this.getLoginInfo(registerDTO.getLoginName()))) {
                    Login newLogin = new Login();
                    BeanUtils.copyProperties(registerDTO, newLogin);
                    loginMapper.insert(newLogin);
                } else {
                    LOGGER.error(String.format("error.loginName.exist:%s", registerDTO.getLoginName()));
                    throw new CommonException("error.loginName.exist");
                }
            }
        } catch (Exception exception) {
            throw new CommonException("error.register:" + exception.getMessage());
        } finally {
            // 释放锁
            rLock.unlock();
        }
        registerDTO.setSuccessFlag(1);
        return registerDTO;
    }
}
